﻿/*********************************************************
 * RouteDebugHelper.cs
 * Author: Husein Rončević, 2013-05-26
 * Displays a table with a list of available routes.
 * For decoration purposes <table> is assigned "routeDebugTable" 
 * CSS class that you can decorate. The third cell in the table is assigned
 * either "routeNotHit" or "routeHit" class. 
 * 
 * In addition to this, there is not unit test as this was written first as 
 * my own practice in analyzing routes and second, it has no business value
 * in the project on which I am working. Enjoy using it and do with it what
 * you want. The only request that I have is to mention that I am the original
 * author.
 * 
 * Best regards to everyone!
 *********************************************************/

using System.Web.Mvc;
using System.Web.Routing;

namespace MvcDeveloper.Infrastructure.HtmlHelpers
{
    public static class RouteDebugHelper
    {
         public static MvcHtmlString RouteDebug(this HtmlHelper html, bool ignoreInReleaseModel = true)
         {
             if (!html.ViewContext.HttpContext.IsDebuggingEnabled && ignoreInReleaseModel)
             {
                 return MvcHtmlString.Create(string.Empty);
             }

             var table = new TagBuilder("table");
             var thead = new TagBuilder("thead");
             var tbody = new TagBuilder("tbody");

             thead.InnerHtml = BuildHeadRow();

             foreach (var routeBase in html.RouteCollection)
             {
                 var route = (Route) routeBase;
                 var routeData = route.GetRouteData(html.ViewContext.HttpContext);

                 tbody.InnerHtml += BuildBodyRow(route, routeData);
             }

             table.InnerHtml += thead;
             table.InnerHtml += tbody;

             table.AddCssClass("routeDebugTable");

             return MvcHtmlString.Create(table.ToString());
         }

        private static string BuildHeadRow()
        {
            var tr = new TagBuilder("tr");
            var thUrl = new TagBuilder("th");
            var thRouteDefinition = new TagBuilder("th");
            var thIsMatch = new TagBuilder("th");

            thUrl.InnerHtml = "URL";
            thRouteDefinition.InnerHtml = "Route Definition";
            thIsMatch.InnerHtml = "Is Match?";

            tr.InnerHtml += thUrl;
            tr.InnerHtml += thRouteDefinition;
            tr.InnerHtml += thIsMatch;

            return tr.ToString();
        }

        private static string BuildBodyRow(Route route, RouteData routeData)
        {
            var tr = new TagBuilder("tr");
            var tdUrl = new TagBuilder("td");
            var tdRouteDefinition = new TagBuilder("td");
            var tdIsMatch = new TagBuilder("td");

            tdUrl.InnerHtml = route.Url;

            tdRouteDefinition.InnerHtml = EnumerateRouteValueDictionary("Defaults", route.Defaults);
            tdRouteDefinition.InnerHtml += "<br />";
            tdRouteDefinition.InnerHtml += EnumerateRouteValueDictionary("Constraints", route.Constraints);
            tdRouteDefinition.InnerHtml += "<br />";
            tdRouteDefinition.InnerHtml += EnumerateRouteValueDictionary("Data Tokens", route.DataTokens);

            tdIsMatch.AddCssClass(routeData == null ? "routeNotHit" : "routeHit");

            tr.InnerHtml = tdUrl.ToString();
            tr.InnerHtml += tdRouteDefinition.ToString();
            tr.InnerHtml += tdIsMatch.ToString();

            return tr.ToString();
        }

        private static string EnumerateRouteValueDictionary(string title, RouteValueDictionary dictionary)
        {
            var span = new TagBuilder("span");
            span.InnerHtml = string.Format("{0}: ", title);

            if (dictionary == null || dictionary.Count == 0)
            {
                span.InnerHtml += "(none)";
            }
            else
            {
                var ul = new TagBuilder("ul");

                foreach (var keyValuePair in dictionary)
                {
                    var li = new TagBuilder("li");
                    var key = keyValuePair.Key;
                    var value = keyValuePair.Value;
                    var completeValue = string.Empty;

                    if (value is string[])
                    {
                        completeValue = string.Join(", ", (value as string[]));

                        if (completeValue.Contains(","))
                        {
                            completeValue = completeValue.Substring(0, completeValue.LastIndexOf(","));
                        }
                    }
                    else
                    {
                        completeValue = value.ToString();
                    }

                    li.InnerHtml = string.Format("- {0}: {1}", key, completeValue);

                    ul.InnerHtml += li.ToString();
                }

                span.InnerHtml += ul.ToString();
            }

            return span.ToString();
        }
    }
}